// CE2 - TextBuddy++

#include <iostream>
#include <string>
#include <fstream>
#include <sstream>
#include <algorithm>
#include <vector>
#include "TextBuddy.h"
#include "../../testing/Tester.h"

using namespace std;

const string TextBuddy::ERROR_NO_FILENAME = "Enter text file name!";
const string TextBuddy::ERROR_INVALID_FILENAME = "Invalid File name!";
const string TextBuddy::ERROR_INVALID_COMMAND = "Please enter a valid command!";
const string TextBuddy::ERROR_SORT_EMPTYFILE = "Cannot sort an empty file!";

const string TextBuddy::COMMAND_ADD = "add";
const string TextBuddy::COMMAND_DISPLAY = "display";
const string TextBuddy::COMMAND_CLEAR = "clear";
const string TextBuddy::COMMAND_DELETE = "delete";
const string TextBuddy::COMMAND_EXIT = "exit";
const string TextBuddy::COMMAND_SORT = "sort";
const string TextBuddy::COMMAND_SEARCH = "search";

const string TextBuddy::MESSAGE_COMMAND = "command: ";
const string TextBuddy::MESSAGE_SORTED = "File sorted!";
const string TextBuddy::MESSAGE_NO_SEARCH_RESULT = "No results found!";
const string TextBuddy::FILENAME_TEMPORARY = "temp.txt";

string TextBuddy::throwErrorMessage(string error){

	stringstream output;
	output<<error<<endl;

	return output.str();
}

void TextBuddy::checkIfNoArguments(int argc){

	if (argc==1){

		cout<<throwErrorMessage(ERROR_NO_FILENAME);
		getchar();
		exit(0);
	}
}

void TextBuddy::checkIfInvalidFileName(char* argv[]){

	fstream filestr;
	filestr.open (argv[1] , ios::app);
	if (!filestr.is_open() ){

		cout<<throwErrorMessage(ERROR_INVALID_FILENAME);
		getchar();
		exit(0);
	}

	filestr.close();
}

void TextBuddy::checkIfInvalidArguments(int argc,char* argv[]){

	checkIfNoArguments(argc);
	checkIfInvalidFileName(argv);
}

string TextBuddy::add(string line,char* argv[]){

	ofstream writeFile(argv[1] , ios::app);
	writeFile<<line<<endl;
	stringstream output;
	output<< "added to " << argv[1] << ": \"" << line << "\"" << endl;
	writeFile.close();
	return output.str();
}

vector<string> TextBuddy::getFileContent(char* argv[]){

	ifstream readFile(argv[1]);
	string str;
	vector<string> fileOutput;

	while(getline(readFile,str)){
		fileOutput.push_back(str);
	}

	readFile.close();

	return fileOutput;
}

string TextBuddy::display(char* argv[]){

	vector<string> fileOutput = getFileContent(argv);
	stringstream output;

	for(int i=0; i < fileOutput.size(); i++){
		output<<i+1<<". "<<fileOutput[i]<<endl;
	}

	if (fileOutput.size()==0){
		output<< argv[1] << " is empty"<<endl;
	}

	return output.str();
}

bool TextBuddy::checkIfFileIsEmpty(vector<string> fileContent){

	if (fileContent.size()==0){
		return true;
	}

	else{
		return false;
	}
}

void TextBuddy::writeToFile(vector<string> fileContent){

	ofstream writeFile(argv[1]);
	for (int i=0;i<fileContent.size();i++){
		writeFile<<fileContent[i]<<endl;
	}
}

//sorts the file content and saves it
string TextBuddy::sortFileContent(char* argv[]){

	vector<string> fileContent = getFileContent(argv);

	if ( checkIfFileIsEmpty(fileContent)){
		return ERROR_SORT_EMPTYFILE;
	}

	sort(fileContent.begin(),fileContent.end());

	writeToFile(fileContent);
	return MESSAGE_SORTED;
}

//function with main algorithm to carry out search
void TextBuddy::processSearch(string searchKeyword,vector<int>& lineNumbers,vector<string> fileContent){

	for (int i=0; i < fileContent.size(); i++){

		stringstream line(fileContent[i]);
		string word;

		while(line>>word){

			if (word==searchKeyword){
				lineNumbers.push_back(i);
			}
		}
	}
}

//case sensitive search for a word
string TextBuddy::searchWord(string searchKeyword, char* argv[]){

	vector<string> fileContent = getFileContent(argv);
	int pos;
	int i;

	//vector to store line nos. of lines in which search keyword was found
	vector<int> lineNumbers;

	stringstream output;

	processSearch(searchKeyword,lineNumbers,fileContent);

	if (lineNumbers.size()==0){

		output<<MESSAGE_NO_SEARCH_RESULT;
		return output.str();
	}

	output<<giveSearchResults(lineNumbers,fileContent);
	return output.str();
}

string TextBuddy::giveSearchResults(vector<int> lineNumbers,vector<string> fileContent){

	stringstream output;

	output<<lineNumbers.size()<<" result";

	//check if search results are more than 1
	if (lineNumbers.size()>1){
		output<<"s";
	}

	output<<" found."<<endl<<"Line";
	if (lineNumbers.size()>1){
		output<<"s";
	}

	output<<" with the keyword -"<<endl;

	for (int i=0; i < lineNumbers.size(); i++){
		output<<lineNumbers[i]+1<<". "<<fileContent[lineNumbers[i]]<<endl;
	}

	return output.str();
}


string TextBuddy::clear(char* argv[]){

	stringstream output;
	ofstream writeFile(argv[1]);
	output<< "all content deleted from " << argv[1] << endl;
	writeFile.close();

	return output.str();
}

void TextBuddy::checkIfLineNumberDoesNotExist(int flag, int lineNumberToDelete, char* argv[], string deletedLine){

	if(flag!=0){
		cout<< "deleted from " << argv[1] << ": \"" << deletedLine << "\"" << endl;
	}

	else{
		cout<< "line number " << lineNumberToDelete << " doesn't exist" << endl;
	}
}

void TextBuddy::deleteLine(int lineNumberToDelete,char* argv[]){

	ifstream readFile(argv[1]);
	ofstream writeFile(FILENAME_TEMPORARY);
	int currentLineNumber=0;
	int flag=0;
	string currentLine;
	string deletedLine;

	while(getline(readFile,currentLine)){
		currentLineNumber++;

		if(currentLineNumber!=lineNumberToDelete){
			writeFile<<currentLine<<endl;
		}

		else if (currentLineNumber==lineNumberToDelete){
			flag++;
			deletedLine=currentLine;
		}
	}
	readFile.close();
	writeFile.close();
	remove(argv[1]);
	rename(FILENAME_TEMPORARY.c_str(),argv[1]);
	checkIfLineNumberDoesNotExist(flag,lineNumberToDelete,argv,deletedLine);
}

string TextBuddy::extractFirstWord (string command){

	int i = 0;
	i = command.find(' ');
	string firstWord = command.substr(0,i);
	return firstWord;
}

string TextBuddy::extractAfterFirstWord (string command, string firstWord){

	string restString;
	restString = command.substr(firstWord.size() + 1); 
	return restString;
}

string TextBuddy::getCommand(){

	string command;
	getline(cin,command);
	return command;
}

void TextBuddy::determineCommand(char* argv[], string command){

	string firstWord = extractFirstWord(command);
	string restString;
	string searchKeyword;

	if(_stricmp(firstWord.c_str() ,COMMAND_ADD.c_str())==0){
		restString = extractAfterFirstWord(command, firstWord);
		cout<<add(restString,argv);
	}

	else if (_stricmp(firstWord.c_str() ,COMMAND_SEARCH.c_str())==0){
		searchKeyword = extractAfterFirstWord(command, firstWord);
		cout<<searchWord(searchKeyword,argv);
	}


	else if (_stricmp(firstWord.c_str(),COMMAND_DISPLAY.c_str()) ==0 && command.size() == firstWord.size() ){
		cout<<display(argv);
	}

	else if (_stricmp(firstWord.c_str(),COMMAND_CLEAR.c_str())==0 && command.size() == firstWord.size() ){
		cout<<clear(argv);
	}

	else if (_stricmp(firstWord.c_str(),COMMAND_SORT.c_str())==0 && command.size() == firstWord.size() ){
		cout<<sortFileContent(argv);
	}

	else if (_stricmp(firstWord.c_str(),COMMAND_DELETE.c_str())==0 && command.size() != firstWord.size() ){
		restString = extractAfterFirstWord(command, firstWord);

		if (atoi(restString.c_str())){
			deleteLine(atoi(restString.c_str()),argv);
		}

		else{
			cout<<throwErrorMessage(ERROR_INVALID_COMMAND);
		}
	}

	else if (strcmpi(firstWord.c_str(),COMMAND_EXIT.c_str()) ==0 && command.size() == firstWord.size() ){
		exit(0);
	}

	else{
		cout<<throwErrorMessage(ERROR_INVALID_COMMAND);
	}

}

void TextBuddy::executeProgram(char* argv[]){

	cout<<endl;
	do{
		cout<<MESSAGE_COMMAND;
		string command = getCommand();
		determineCommand(argv, command);
	}while(1);
}

void TextBuddy::printFileReady(char* argv[]){

	cout << "Welcome to " << argv[0] << ". "<< argv[1] << " is ready for use";
}


int main( int argument_count, char** argument_vars ){

	runTest(argument_count, argument_vars); //run unit test. comment out to disable

	TextBuddy TB;
	int argc=2;
	char *argv[]={"TextBuddy","mytextfile.txt"};
	TB.checkIfInvalidArguments(argc,argv);
	TB.printFileReady(argv);
	TB.executeProgram(argv);
	return 0;

}